﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Common
{
    public sealed class CsvReader : IDisposable
    {
        #region 常量

        #endregion

        #region 私有变量

        private string _rowSeparator = "{CR}{LF}";
        private string _columnSeparator = ",";
        private string _textQualifier;

        private FileStream _fileStream;
        private Stream _stream;
        private StreamReader _streamReader;
        private StreamWriter _streamWriter;
        private Stream _memoryStream;
        private Encoding _encoding;
        private readonly StringBuilder _columnBuilder = new StringBuilder(100);
        private readonly Type _type = Type.File;
        #endregion

        #region 公共属性

        public bool HasHeaderRow { get; set; }

        /// <summary>
        /// 获取或设置列值是否应修剪
        /// </summary>
        public bool TrimColumns { get; set; }

        public readonly List<string> Headers = new List<string>();

        /// <summary>
        /// 如果没有记录被读取，返回一个集合字段或空
        /// </summary>
        public List<string> Fields { get; private set; }

        public int HeaderCount
        {
            get
            {
                return Headers.Count;
            }
        }

        public int FieldCount
        {
            get
            {
                return Fields.Count;
            }
        }

        #region 索引
        public string this[int fieldIndex]
        {
            get
            {

                if (fieldIndex > (this.Fields.Count - 1))
                    throw new IndexOutOfRangeException(string.Format("列索引 {0} 超出了列数 {1}.", fieldIndex, this.Fields.Count));
                return this.Fields[fieldIndex];
            }
        }

        public string this[string fieldName]
        {
            get
            {
                int fieldIndex = -1;
                for (int i = 0; i < Headers.Count; i++)
                {
                    if (string.Compare(Headers[i], fieldName) != 0)
                        continue;

                    fieldIndex = i;
                    break;
                }
                if (fieldIndex == -1)
                    throw new ArgumentException(string.Format("字段 '{0}' 不存在", fieldName));

                if (fieldIndex > (this.Fields.Count - 1))
                {
                    //throw new IndexOutOfRangeException(string.Format("列索引 {0} 超出了列数 {1}.", fieldIndex, recordIndex));
                    return null;
                }
                return this.Fields[fieldIndex];
            }
        }

        #endregion Indexers

        #endregion

        #region 构造函数

        public CsvReader(string filePath)
        {
            _type = Type.File;
            Initialise(filePath, Encoding.Default);
        }

        public CsvReader(string filePath, Encoding encoding)
        {
            _type = Type.File;
            Initialise(filePath, encoding);
        }

        public CsvReader(Stream stream)
        {
            _type = Type.Stream;
            Initialise(stream, Encoding.Default);
        }

        public CsvReader(Stream stream, Encoding encoding)
        {
            _type = Type.Stream;
            Initialise(stream, encoding);
        }
        public CsvReader(Stream stream, Encoding encoding, bool hasHeaderRow)
        {
            _type = Type.Stream;
            HasHeaderRow = hasHeaderRow;
            Initialise(stream, encoding);
        }
        public CsvReader(Stream stream, Encoding encoding, bool hasHeaderRow, string rowSeparator, string columnSeparator)
        {
            _type = Type.Stream;
            _rowSeparator = rowSeparator;
            _columnSeparator = columnSeparator;
            HasHeaderRow = hasHeaderRow;
            Initialise(stream, encoding);
        }

        public CsvReader(Encoding encoding, string csvContent)
        {
            _type = Type.String;
            Initialise(encoding, csvContent);
        }
        #endregion

        #region 公共函数

        /// <summary>
        /// 读取下一条记录
        /// </summary>
        /// <returns></returns>
        public void SeekFirstRecord()
        {
            Fields = null;
            _streamReader.BaseStream.Seek(0, SeekOrigin.Begin);
            if (this.HasHeaderRow == true)
            {
                _streamReader.ReadLine();
            }
        }

        /// <summary>
        /// 读取下一条记录
        /// </summary>
        /// <returns></returns>
        public bool ReadNextRecord()
        {
            Fields = null;
            string line = _streamReader.ReadLine();
            if (line == null)
                return false;
            ParseLine(line);
            return true;
        }
        #endregion

        #region IDisposable 接口函数
        /// <summary>
        /// Disposes of all unmanaged resources
        /// </summary>
        public void Dispose()
        {
            if (_streamReader != null)
            {
                _streamReader.Close();
                _streamReader.Dispose();
            }

            if (_streamWriter != null)
            {
                _streamWriter.Close();
                _streamWriter.Dispose();
            }

            if (_memoryStream != null)
            {
                _memoryStream.Close();
                _memoryStream.Dispose();
            }

            if (_fileStream != null)
            {
                _fileStream.Close();
                _fileStream.Dispose();
            }

            if ((_type == Type.String || _type == Type.File) && _stream != null)
            {
                _stream.Close();
                _stream.Dispose();
            }
        }
        #endregion

        #region 私有函数
        private void Initialise(string filePath, Encoding encoding)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException(string.Format("文件 '{0}' 不存在.", filePath));

            _fileStream = File.OpenRead(filePath);
            Initialise(_fileStream, encoding);
        }

        private void Initialise(Stream stream, Encoding encoding)
        {
            if (stream == null)
                throw new ArgumentNullException("提供的流是空的.");

            _stream = stream;
            _stream.Position = 0;
            _encoding = (encoding ?? Encoding.Default);
            _streamReader = new StreamReader(_stream, _encoding);

            this.InitialiseHead();
        }

        private void Initialise(Encoding encoding, string csvContent)
        {
            if (csvContent == null)
                throw new ArgumentNullException("提供的csvContent的是空的.");

            _encoding = (encoding ?? Encoding.Default);

            _memoryStream = new MemoryStream(csvContent.Length);
            _streamWriter = new StreamWriter(_memoryStream);
            _streamWriter.Write(csvContent);
            _streamWriter.Flush();
            Initialise(_memoryStream, encoding);
        }

        /// <summary>
        /// 解析一个CSV文件的行
        /// </summary>
        /// <param name="line">Line</param>
        private void ParseLine(string line)
        {
            Fields = new List<string>();
            bool inColumn = false;
            bool inQuotes = false;
            _columnBuilder.Remove(0, _columnBuilder.Length);


            var lineAyyay = line.Split(_columnSeparator.ToCharArray());
            foreach (var item in lineAyyay)
            {
                char character =item.Length>0? item[0]:'\0';
                if (character == '"')
                {
                    // 引号，尝试匹配下一个
                    if (item.IndexOf('"', 1) > 0)
                    {
                        // 如果当前字段中有匹配的引号，则成功
                        _columnBuilder.Append(item);
                        inColumn = true;
                    }
                    else
                    {
                        _columnBuilder.Append(item);
                        inColumn = false;
                    }
                }
                else
                {
                    // 如果当前字段中有匹配的引号，则成功
                    _columnBuilder.Append(item);
                    inColumn = true;
                }
                if (inColumn)
                {
                    Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString());
                    _columnBuilder.Remove(0, _columnBuilder.Length);
                }

            }

            //// 遍历行中的每个字符
            //for (int i = 0; i < line.Length; i++)
            //{
            //    char character = line[i];
            //    if (!inColumn)
            //    {
            //        // 如果当前字符是双引号，则该列的值包含在双引号，否则追加下一个字符
            //        if (character == '"')
            //            inQuotes = true;
            //        else
            //            _columnBuilder.Append(character);

            //        inColumn = true;
            //        continue;
            //    }

            //    // 如果我们在双引号之间
            //    if (inQuotes)
            //    {
            //        // 如果当前字符是双引号，下一个字符是逗号或行尾
            //        if (character == '"' && ((line.Length > (i + 1) && line[i + 1] == ',') || ((i + 1) == line.Length)))
            //        {
            //            inQuotes = false;
            //            inColumn = false;
            //            i++;
            //        }
            //        else if (character == '"' && line.Length > (i + 1) && line[i + 1] == '"')
            //            i++;
            //    }
            //    else if (character == ',')
            //        inColumn = false;

            //    if (!inColumn)
            //    {
            //        Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString());
            //        _columnBuilder.Remove(0, _columnBuilder.Length);
            //    }
            //    else // 追加当前列
            //        _columnBuilder.Append(character);
            //}
            //if (inColumn)
            //    Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString());
        }

        private void ParseLine1(string line)
        {
            Fields = new List<string>();
            bool inColumn = false;
            bool inQuotes = false;
            _columnBuilder.Remove(0, _columnBuilder.Length);

            // 遍历行中的每个字符
            for (int i = 0; i < line.Length; i++)
            {
                char character = line[i];
                if (!inColumn)
                {
                    // 如果当前字符是双引号，则该列的值包含在双引号，否则追加下一个字符
                    if (character == '"')
                        inQuotes = true;
                    else
                        _columnBuilder.Append(character);

                    inColumn = true;
                    continue;
                }

                // 如果我们在双引号之间
                if (inQuotes)
                {
                    // 如果当前字符是双引号，下一个字符是逗号或行尾
                    if (character == '"' && ((line.Length > (i + 1) && line[i + 1] == ',') || ((i + 1) == line.Length)))
                    {
                        inQuotes = false;
                        inColumn = false;
                        i++;
                    }
                    else if (character == '"' && line.Length > (i + 1) && line[i + 1] == '"')
                        i++;
                }
                else if (character == ',')
                    inColumn = false;

                if (!inColumn)
                {
                    Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString());
                    _columnBuilder.Remove(0, _columnBuilder.Length);
                }
                else // 追加当前列
                    _columnBuilder.Append(character);
            }
            if (inColumn)
                Fields.Add(TrimColumns ? _columnBuilder.ToString().Trim() : _columnBuilder.ToString());
        }


        private void InitialiseHead()
        {
            Headers.Clear();
            bool addedHeader = false;
            if (this.HasHeaderRow == true)
            {
                while (this.ReadNextRecord())
                {
                    if (this.HasHeaderRow && !addedHeader)
                    {
                        this.Fields.ForEach(field => Headers.Add(field));
                        addedHeader = true;
                        break;
                        //continue;
                    }
                }
            }
        }
        #endregion

        #region 枚举

        private enum Type
        {
            File,
            Stream,
            String
        }
        #endregion Enums
    }
}
